/*
 * @Author: fhw
 * @Date: 2022-07-18 10:30:37
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2023-02-07 14:30:58
 * @Description:
 */
import {
  HttpException,
  HttpStatus,
  Injectable,
  UseFilters,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Between, LessThan, Like, MoreThan, Repository } from 'typeorm';
import {
  ArticleIdAndTitle,
  ArticleInfo,
  ArticleList,
  ArticleResult,
} from './entities/article.entity';

import { HttpExceptionFilter } from '../http-exception.filter';
import { clone, equals, isEmpty, isNil } from 'ramda';
import { getPagination, throwHttp, timestampToTime } from '../utlis/index';
import {
  UpdateArticleThumbsUpDto,
  ArticlePageDto,
  CreateUpdateArticleDto,
  ArticleInfoDto,
} from './dto/article.dto';
import { IdDto } from '../common/dto/dto';
import { Tags } from '../article_tag/entities/article_tag.entity';
import { ChatUser } from 'src/chat_user/entities/chat_user.entity';
const reg = '!\\[.*\\]\\(.*\\)';
@Injectable()
export class ArticleService {
  constructor(
    @InjectRepository(ArticleList)
    private articleRepository: Repository<ArticleList>,
    @InjectRepository(Tags)
    private articleTagsRepository: Repository<Tags>,
  ) { }
  @UseFilters(new HttpExceptionFilter())
  async getArticlePage(params: ArticlePageDto): Promise<ArticleResult> {
    const {
      title,
      tags,
      category,
      status,
      createTimeStart,
      createTimeEnd,
      userId
    } = params;
    const { pageSize, pageNum } = getPagination({ pageSize: params.pageSize, pageNum: params.pageNum })
    const where: ArticlePageDto = {
      title: Like(`%${title || ''}%`),
      tags: Like(`%${tags || ''}%`),
      category,
      status,
      userId
    };
    // 时间筛选
    if (!isNil(createTimeStart) && isNil(createTimeEnd)) {
      where.createDate = Between(createTimeStart, createTimeEnd);
    }
    const [data, total] = await this.articleRepository.findAndCount({
      where,
      skip: pageNum * pageSize,
      take: pageSize,
      order: { createDate: 'DESC' },
      relations: ['comment', 'user'],
      cache: true,
    });
    return {
      data: data.map((item) => {
        return {
          ...item,
          content: item.content.replace(new RegExp(reg, 'g'), '').slice(0, 300),
          tags: JSON.parse(item.tags),
          comment: item.comment.length as any,
        };
      }),
      total,
      pageSize,
      pageNum: pageNum * pageSize + 1,
    };
  }
  // 最新文章
  @UseFilters(new HttpExceptionFilter())
  async getArticleLately(): Promise<ArticleIdAndTitle[]> {
    return await this.articleRepository.find({
      skip: 0,
      take: 5,
      where: { status: 1 },
      order: { id: 'DESC' },
      select: ['id', 'title'],
    });
  }
  // 热门文章
  @UseFilters(new HttpExceptionFilter())
  async getArticleHot(): Promise<ArticleIdAndTitle[]> {
    const data = await this.articleRepository.find({
      skip: 0,
      take: 5,
      where: { status: 1 },
      order: { read: 'DESC' },
      select: ['id', 'title'],
    });
    return data
  }
  // 文章详情
  @UseFilters(new HttpExceptionFilter())
  async getArticleInfo({ id, category }: ArticleInfoDto): Promise<ArticleInfo> {
    const data = await this.articleRepository.findOne({
      where: { id },
      relations: ['user'],
    });
    if (isNil(data) || isEmpty(data)) {
      throwHttp('文章不存在，请检查id是否正确')
    }
    try {
      data.tags = JSON.parse(data.tags)
    } catch (error) {
      data.tags = data.tags
    }
    const prev = await this.articleRepository.findOne({
      where: {
        category,
        id: LessThan(id),
        status: 1
      },
      select: ['id', 'title'],
      order: { createDate: 'DESC' },
    });
    const next = await this.articleRepository.findOne({
      where: {
        category,
        id: MoreThan(id),
        status: 1
      },
      select: ['id', 'title'],
      order: { createDate: 'DESC' },
      cache: true,
    });
    return {
      data,
      prev,
      next,
    };
  }
  // 更新点赞数
  @UseFilters(new HttpExceptionFilter())
  async updateArticleThumbsUp({
    id,
  }: UpdateArticleThumbsUpDto): Promise<string> {
    const { data } = await this.getArticleInfo({ id });
    try {
      await this.articleRepository
        .createQueryBuilder()
        .update('article')
        .set({
          thumbUp: data.thumbUp + 1,
        })
        .where('id = :id', { id })
        .execute();
      return '更新成功';
    } catch (error) {
      throwHttp(error.message)
    }
  }
  // 创建更新文章入口
  @UseFilters(new HttpExceptionFilter())
  async createUpdateArticle(params: CreateUpdateArticleDto, { user }: { user: ChatUser }): Promise<{ msg: string, data: ArticleList }> {
    const { id } = params;
    try {
      let result = null
      await this.tagIntercept(params);
      if (isNil(id)) {
        result = await this.createArticle(params, { user });
      } else {
        result = await this.updateArticle(params, { user });
      }
      return {
        msg: '更新成功',
        data: result
      };
    } catch (error) {
      throwHttp(error.message)
    }
  }
  // 创建文章
  @UseFilters(new HttpExceptionFilter())
  async createArticle(params: CreateUpdateArticleDto, { user }: { user: ChatUser }) {
    try {
      const article = new ArticleList();
      if (equals(params.status, 1)) {
        if (isNil(params.title) || isEmpty(params.title)) {
          throwHttp('标题不能为空')
        }
        if (isNil(params.content) || isEmpty(params.content)) {
          throwHttp('文章内容不能为空')
        }
      }
      article.title = params.title;
      article.content = params.content;
      article.tags = JSON.stringify(params.tags);
      article.abstract = params.abstract;
      article.category = params.category;
      article.imageUrl = params.imageUrl;
      article.status = params.status;
      article.userId = user.id as number;
      article.user = user;
      const result = await this.articleRepository.manager.save(article);
      return result;
    } catch (error) {
      throwHttp(error.message)
    }
  }
  // 更新文章
  @UseFilters(new HttpExceptionFilter())
  async updateArticle(params: CreateUpdateArticleDto, { user }: { user: ChatUser }): Promise<ArticleList> {
    try {
      const { id } = params;
      const result = await this.getArticleInfo({ id });
      const values = {
        title: params.title,
        content: params.content,
        tags: JSON.stringify(params.tags),
        abstract: params.abstract,
        category: params.category,
        imageUrl: params.imageUrl,
        status: params.status,
        user: user
      };
      const data = await this.articleRepository
        .createQueryBuilder()
        .update('article')
        .set(values)
        .where('id = :id', { id })
        .execute();
      if (data.affected) {
        return result.data;
      }
      throwHttp('更新文章失败')
    } catch (error) {
      throwHttp(error.message)
    }
  }
  // 删除文章
  @UseFilters(new HttpExceptionFilter())
  async deleteArticle({ id }: IdDto) {
    try {
      const { data } = await this.getArticleInfo({ id });
      await this.articleRepository.remove(data);
      return '删除成功';
    } catch (error) {
      throwHttp(error.message)
    }
  }
  @UseFilters(new HttpExceptionFilter())
  async tagIntercept(params: CreateUpdateArticleDto): Promise<boolean> {
    // 拦截标签
    const tags = await this.articleTagsRepository.find({
      select: ['name'],
    });
    const tagsName = tags.map((item) => item.name);
   const newTag =  params.tags?.filter((item) => {
      return !tagsName.includes(item);
    }).map(item => {
      const tags = new Tags()
      tags.name = item
      return tags
    });
    if (!isEmpty(newTag)) {
      await this.articleTagsRepository.manager.createQueryBuilder().insert().into(Tags).values(newTag).execute()
    }
    return true;
  }
  // 更新阅读量
  @UseFilters(new HttpExceptionFilter())
  async updateArticleReadCount({ id }: IdDto) {
    const { data } = await this.getArticleInfo({ id });
    try {
      await this.articleRepository
        .createQueryBuilder()
        .update('article')
        .set({
          read: data.read + 1,
        })
        .where('id = :id', { id })
        .execute();
      return '更新成功';
    } catch (error) {
      throwHttp(error.message)
    }
  }
}
